package com.wtwd.device.service.impl;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.wtwd.device.constant.RespCode;
import com.wtwd.device.constant.SeparatorConstant;
import com.wtwd.device.dao.CustomerMapper;
import com.wtwd.device.dao.DeviceInfoMapper;
import com.wtwd.device.dao.DeviceSettingMapper;
import com.wtwd.device.dao.SysRoleMapper;
import com.wtwd.device.model.entity.*;
import com.wtwd.device.model.vo.CountAreaDeviceVO;
import com.wtwd.device.model.vo.ExcleExportVo;
import com.wtwd.device.model.vo.RespEntity;
import com.wtwd.device.model.vo.WatchVo;
import com.wtwd.device.service.DeviceInfoService;
import com.wtwd.device.util.DateUtils;
import com.wtwd.device.utils.MyExcelUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.util.*;
import java.util.concurrent.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * @author mjy
 * @date 2021/08/06
 * @description
 */
@Service
public class DeviceInfoServiceImpl implements DeviceInfoService {

    private static final Logger logger = LoggerFactory.getLogger(DeviceInfoServiceImpl.class);

    @Autowired
    private DeviceInfoMapper deviceInfoMapper;
    @Autowired
    private CustomerMapper customerMapper;
    @Autowired
    private SysRoleMapper sysRoleMapper;
    @Autowired
    private DeviceSettingMapper deviceSettingMapper;

    private static ExecutorService threadPool = new ThreadPoolExecutor(1,
            5,
            1L,
            TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(4),
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.AbortPolicy());

    @Override
    public IPage<WatchVo> getWatchListByConditions(HashMap<String, Object> searchMap, Page<Object> page) {
        return deviceInfoMapper.getWatchListByConditions(searchMap, page);
    }

    @Override
    public Integer bathDelDeviceByDeviceIdList(List<Integer> deviceIdList) {
        return deviceInfoMapper.bathDelDeviceByDeviceIdList(deviceIdList);
    }

    @Override
    public Integer deleteDeviceByDeviceId(Integer deviceId) {
        return deviceInfoMapper.deleteDeviceByDeviceId(deviceId);
    }

    @Override
    public RespEntity importDeviceInfo(MultipartFile file, SysUser sysUser, HttpServletResponse response) {
        //导出失败信息记录
        List<ExcelImportDeviceInfo> failMessages = new ArrayList<>();
        //导出失败文件信息
        String path = "";
        //记录需要添加设备信息
        List<Device> deviceInfos = new ArrayList<>();
        //读取文件信息
        List<ExcelImportDeviceInfo> excelImportDeviceInfos = null;
        try {
            excelImportDeviceInfos = MyExcelUtils.importExcel(file, 1, 1, ExcelImportDeviceInfo.class);
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (excelImportDeviceInfos != null && excelImportDeviceInfos.size() > 100000) {
            return new RespEntity(RespCode.IMPORT_FILE_EXCEED_MAX);
        }
        excelImportDeviceInfos = excelImportDeviceInfos.stream().filter(e -> {
            if (StringUtils.isBlank(e.getMacAddress()) || e.getCustomerId() == null) {
                ExcelImportDeviceInfo excelImportDeviceInfo = new ExcelImportDeviceInfo();
                excelImportDeviceInfo.setMacAddress(e.getMacAddress());
                excelImportDeviceInfo.setMessage("必传参数为空");
                failMessages.add(excelImportDeviceInfo);
                return false;
            } else {
                Device watch = new Device();
                //mac地址英文字母只记录大写
                watch.setMacAddress(e.getMacAddress().trim().toUpperCase());
                watch.setCustomerId(e.getCustomerId());
                watch.setCreateTime(new Date().getTime());
                watch.setUpdateTime(new Date().getTime());
                watch.setActivation(0);
                deviceInfos.add(watch);
                return true;
            }
        }).collect(Collectors.collectingAndThen(Collectors.toCollection(() ->
                new TreeSet<>(Comparator.comparing(ExcelImportDeviceInfo::getMacAddress))), ArrayList::new));
        threadPool.submit(() -> {
            logger.info("线程:" + Thread.currentThread().getName() + " 开始执行");
            //批量添加设备
            if (deviceInfos != null && deviceInfos.size() > 0) {
                batchSaveDevice(deviceInfos);
            }
            logger.info("线程:" + Thread.currentThread().getName() + "指令完毕");
        });
        //导出失败文件信息
        Integer failNum = 0;
        if (failMessages != null && failMessages.size() > 0) {
            failNum = failMessages.size();
            path = MyExcelUtils.exportExcel(failMessages, "设备信息", "设备信息", ExcelImportDeviceInfo.class, "设备文件导入失败信息.xls", response);
        }
        //构建返回值集合
        HashMap<String, Object> restMap = new HashMap<>();
        restMap.put("path", path);
        restMap.put("failNum", failNum);
        restMap.put("successNum", deviceInfos != null ? deviceInfos.size() : 0);
        return new RespEntity(RespCode.SUCCESS, restMap);
    }

    @Override
    public Boolean checkWatchIdList(List<String> watchIdList) {
        List<String> list = watchIdList.stream().distinct().collect(Collectors.toList());//去重
        ArrayList<String> lists = deviceSettingMapper.getWatchByGroupWatchId(watchIdList);
        if (lists != null && lists.size() == list.size()) {
            return true;
        }
        return false;
    }

    @Override
    public List<String> getWatchIdByGroupByCustomerId(Long customerId) {
        return deviceInfoMapper.getWatchIdByGroupByCustomerId(customerId);
    }

    @Override
    public RespEntity activeDevice(String macAddress, String watchId, String currentFirmware, Integer activation) {
        Integer rest = deviceInfoMapper.activeDevice(macAddress, watchId, currentFirmware, activation);
        return new RespEntity(rest > 0 ? RespCode.SUCCESS : RespCode.FAILED);
    }

    @Override
    public Integer countDeviceByCustomerId(Long customerId) {
        return new LambdaQueryChainWrapper<>(deviceInfoMapper)
                .eq(customerId != null, Device::getCustomerId, customerId)
                .count();
    }

    @Override
    public Integer countActivatedDeviceByCustomerId(Long customerId) {
        return new LambdaQueryChainWrapper<>(deviceInfoMapper)
                .eq(customerId != null, Device::getCustomerId, customerId)
                .eq(Device::getActivation, 1)
                .count();
    }

    @Override
    public List<CountAreaDeviceVO> countAreaDeviceByCustomerId(Long customerId) {
        return deviceInfoMapper.countAreaDeviceByCustomerId(customerId, 1);
    }

    @Override
    public RespEntity exportDeviceInfo(String macAddress, Integer activation, String customerName, Long customerId, HttpServletResponse response) {
        List<ExportDevice> exportDevices = deviceInfoMapper.exportDeviceInfo(macAddress, activation, customerName, customerId);
        String path = "";
        if (exportDevices != null && exportDevices.size() > 0) {
            exportDevices.stream().forEach(exportDevice -> {
                //替换数据
                if (exportDevice.getActivation().equals(0)) {
                    exportDevice.setIsActivation("否");
                    exportDevice.setActiveDeviceTime("尚未激活");
                } else if (exportDevice.getActivation().equals(1)) {
                    exportDevice.setIsActivation("是");
                    if (exportDevice.getActiveTime() != null && exportDevice.getActiveTime() != 0) {
                        exportDevice.setActiveDeviceTime(DateUtils.getDateStr(exportDevice.getActiveTime(), DateUtils.dateFormat4));
                    } else {
                        exportDevice.setActiveDeviceTime("该手表激活在上个web端版本完成，未记录时间");
                    }
                }
                exportDevice.setImportTime(DateUtils.getDateStr(exportDevice.getCreateTime(), DateUtils.dateFormat4));
            });
            path = MyExcelUtils.exportExcel(exportDevices, "设备信息", "设备信息", ExportDevice.class, "设备信息.xls", response);
        } else {
            new RespEntity(RespCode.CUSTOMER_NOT_DEVICE);
        }
        ExcleExportVo excleExportVo = new ExcleExportVo();
        excleExportVo.setPath(path);
        return new RespEntity(RespCode.SUCCESS, excleExportVo);
    }

    @Override
    public void unBindDeviceByMac(String mac) {
        deviceInfoMapper.unbindDevice(mac);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean batchSaveDevice(List<Device> deviceInfos) {
        if (deviceInfos == null || deviceInfos.size() == 0) {
            return false;
        }
        //循环次数
        List<List<Device>> split = split(deviceInfos, 20000);
        for (List<Device> devices : split) {
            deviceInfoMapper.batchSaveDevice(devices);
        }
        return false;
    }

    @Override
    public IPage<Device> getWatchListByConditionsPage(String macAddress, Integer activation, Long customerId, List<Long> customerIds, Page<Device> page) {
        return deviceInfoMapper.getWatchListByConditionsPage(macAddress,activation,customerId,customerIds,page);
    }

    public static <T> List<List<T>> split(List<T> list, int batchSize) {
        List<List<T>> res = new ArrayList<>();
        int k = 0;
        List<T> temp = new ArrayList<>();
        for (T t : list) {
            temp.add(t);
            k++;
            if (k >= batchSize) {
                k = 0;
                res.add(temp);
                temp = new ArrayList<>();
            }
        }
        if (!temp.isEmpty()) {
            res.add(temp);
        }
        return res;
    }

    /**
     * 根据范围批量添加设备信息
     *
     * @param deviceInfoByRangeDto
     * @return
     */
    @Override
    public RespEntity batchImportDeviceInfoByRange(DeviceInfoByRangeDto deviceInfoByRangeDto) {
        if (deviceInfoByRangeDto == null) {
            return new RespEntity(RespCode.NO_PARAMETERS);
        }
        String start = deviceInfoByRangeDto.getStartMacAddress();
        String end = deviceInfoByRangeDto.getEndMacAddress();
        Long customerId = deviceInfoByRangeDto.getCustomerId();

        if(start == null || "".equals(start)){
            return new RespEntity(RespCode.NO_PARAMETERS);
        }
        if(end == null || "".equals(end)){
            return new RespEntity(RespCode.NO_PARAMETERS);
        }
        if(customerId == null){
            return new RespEntity(RespCode.NO_PARAMETERS);
        }

        //校验格式
        String regex = "[0-9A-F]{2}:[0-9A-F]{2}:[0-9A-F]{2}:[0-9A-F]{2}:[0-9A-F]{2}:[0-9A-F]{2}";
        Pattern p = Pattern.compile(regex);
        Matcher startMactchre = p.matcher(start);
        Matcher endMactchre = p.matcher(end);
        if (!startMactchre.find() || !endMactchre.find()) {
            return new RespEntity(RespCode.NO_SPECIFICATIOS);
        }

        long startNum = Long.parseLong(start.replaceAll(SeparatorConstant.COLON, ""), 16);
        long endNum = Long.parseLong(end.replaceAll(SeparatorConstant.COLON, ""), 16);
        if(startNum > endNum){
            return new RespEntity(RespCode.START_MORE_END);
        }
        if((endNum - startNum) > 100000L){
            return new RespEntity(RespCode.IMPORT_FILE_EXCEED_MAX);
        }

        List<String> hexadecimalList = batchGenerateHexadecimal(start, end);

        if(hexadecimalList == null || hexadecimalList.size() <= 0){
            return new RespEntity(RespCode.DATA_DISORDER);
        }
        //记录需要添加设备信息
        List<Device> deviceInfos = new ArrayList<>();
        for (String s : hexadecimalList) {
            Device device = new Device();
            device.setMacAddress(s);
            device.setCustomerId(customerId);
            device.setCreateTime(System.currentTimeMillis());
            device.setUpdateTime(System.currentTimeMillis());
            device.setActivation(0);
            deviceInfos.add(device);
        }
        threadPool.submit(() -> {
            logger.info("线程:" + Thread.currentThread().getName() + " 开始执行");
            //批量添加设备
            if (deviceInfos != null && deviceInfos.size() > 0) {
                batchSaveDevice(deviceInfos);
            }
            logger.info("线程:" + Thread.currentThread().getName() + "指令完毕");
        });
        return new RespEntity(RespCode.SUCCESS);
    }

    /**
     * 批量生成十六进制数
     *
     * @param start 范围起始数
     * @param end   范围结束数
     * @return
     */
    private static List<String> batchGenerateHexadecimal(String start, String end) {
        char[] hexadecimalChars = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
        //                          0    1    2    3    4    5    6    7    8    9    10   11   12   13   14   15
        char[] startChars = start.toCharArray();
        int[] startCharsIndexCount = new int[startChars.length];

        //记录startChars数组字符在hexadecimalChars出现的位置 并把对应下标存放到startCharsIndexCount中
        for (int i = 0; i < startChars.length; i++) {
            //该位置使用默认值
            if (startChars[i] == ':') {
                continue;
            }
            for (int j = 0; j < hexadecimalChars.length; j++) {
                if (startChars[i] == hexadecimalChars[j]) {
                    startCharsIndexCount[i] = j;
                    break;
                }
            }
        }
        //'F'左移一位 并维护数组
        List<String> list = new ArrayList<>();
        list.add(start);
        int lowByte = startChars.length - 1;
        while (!end.equals(start)) {
            while (startChars[lowByte] == 'F') {
                startChars[lowByte] = '0';
                //维护startCharsIndexCount[lowByte]下标数据
                startCharsIndexCount[lowByte] = (startCharsIndexCount[lowByte] + 1) % hexadecimalChars.length;

                lowByte--;
                lowByte = startChars[lowByte] == ':' ? lowByte - 1 : lowByte;
            }
            //维护startCharsIndexCount[lowByte]下标数据
            startCharsIndexCount[lowByte] = (startCharsIndexCount[lowByte] + 1) % hexadecimalChars.length;
            startChars[lowByte] = hexadecimalChars[startCharsIndexCount[lowByte]];
            start = new String(startChars);
            list.add(start);
            lowByte = startChars.length - 1;
        }
        return list;
    }

}



